package cn.webestar.scms.microsvc.context;

import cn.hutool.core.lang.ClassScanner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @author zgs
 */
@Slf4j
public class MicrosvcApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationContextAware {

    private ApplicationContext context;

    private Set<String> basePackages = new HashSet<>();

    public MicrosvcApplicationContextInitializer(String... basePackages) {
        if (basePackages != null && basePackages.length > 0) {
            this.basePackages.addAll(Arrays.asList(basePackages));
        } else {
            this.basePackages.add("");
        }
    }

    @Override
    public void initialize(ConfigurableApplicationContext context) {
        this.context = context;
        ConfigurableEnvironment env = context.getEnvironment();
        String appName = env.getProperty("spring.application.name");
        String version = env.getProperty("app.version");
        String buildTime = env.getProperty("app.build.timestamp");
        log.info("App info: appName={}, version={}, buildTime={}", appName, version, buildTime);
        checkDependencies();
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.context = context;
    }

    /**
     * 微服务器启动前检查依赖，主要是禁止微服务之间的依赖
     */
    public void checkDependencies() {

        Set<Class<?>> efcClasses = new HashSet<>();
        // 查找出所有 EnableFeignClients Class
        this.basePackages.forEach(pkg -> efcClasses.addAll(ClassScanner.scanPackageByAnnotation(pkg, EnableFeignClients.class)));

        // 获取所有 FeignClient 的所有扫描包
        Set<String> fcPkgs = new HashSet<>();
        if (!efcClasses.isEmpty()) {
            efcClasses.forEach(efcClass -> {
                EnableFeignClients annotation = efcClass.getAnnotation(EnableFeignClients.class);
                fcPkgs.addAll(Arrays.asList(annotation.basePackages()));
            });
        } else {
            fcPkgs.addAll(this.basePackages);
        }

        // 扫描出所有的 FeignClient
        Set<String> feignClientNames = new HashSet<>();
        Set<Class<?>> fcClasses = new HashSet<>();
        fcPkgs.forEach(pkg -> fcClasses.addAll(ClassScanner.scanPackageByAnnotation(pkg, FeignClient.class)));
        fcClasses.forEach(fcClass -> {
            FeignClient annotation = fcClass.getAnnotation(FeignClient.class);
            feignClientNames.add(annotation.name());
        });

        StringBuilder sb = new StringBuilder("FeignClinets[ ");
        feignClientNames.forEach(item -> {
            sb.append(item).append(" ");
        });
        sb.append("]，微服务之间禁止依赖，请检查依赖配置。");

        if (feignClientNames.size() > 1) {
            throw new RuntimeException(sb.toString());
        }

    }

}